// Copyright (c) 2008 Matthew Johnson
// Licensed under the MIT License (see license.txt)
package actionml.interaction {
	import actionml.Global;

	import flash.display.DisplayObject;
	import flash.display.FrameLabel;
	import flash.display.MovieClip;
	import flash.events.FocusEvent;
	import flash.events.MouseEvent;

	public class ButtonInteractions {
		public static function initialize() {
			Global.initializeExtension(ButtonInteractions, DisplayObject);
		}
		public static var instanceMethods:Object = {
			followLinkOnClick: function() {
				if (this.isAlready("FollowingLinkOnClick")) return;
				var o:Object = this;
				this.makeButton();
				this.hit.addEventListener(MouseEvent.CLICK, function(e:MouseEvent) {
					o.followLink();
				});
			},
			makeCheckbox: function(options:Object = null) { 
				if (this.isAlready("Checkbox")) return;
				this.makeStateButton(options);
				options ||= {};
				var groupName:String = options["groupName"] || "defaultCheckboxGroup";
				Global.specialKeys["checkboxGroups"] ||= {};
				this.root["checkboxGroups"] ||= Global.specialKeys["checkboxGroups"];
				this.root["checkboxGroups"][groupName] ||= {};
				this.root["checkboxGroups"][groupName].name = groupName;
				this.root["checkboxGroups"][groupName].isAlready("CheckboxGroup");
				this.root["checkboxGroups"][groupName].buttons ||= [];
				this.root["checkboxGroups"][groupName].buttons.push(this);
				this.checkboxGroup = this.root["checkboxGroups"][groupName];
				this.getUnchecked = function() {
					return this.getState() != "checked";
				}
				this.getChecked = function() {
					return this.getState() == "checked";
				}
				this.setChecked = function(v:Object) {
					if (v && this.getState() != "checked") {
						this.setState("checked");
					} else if (!v && this.getState() == "checked") {
						this.setState("unchecked");
					}
				}
				this.check = function() {
					this.setChecked(true);
				}
				this.uncheck = function() {
					this.setChecked(false);
				}
				this.setChecked("checked" in options && options["checked"]);
				this.root["checkboxGroups"][groupName].getValueForMissingKey = function(k:String) {
					var values:Array = [];
					var allNumbers:Boolean = true;
					for each (var b:Object in this.buttons) {
						if (b.getChecked()) {
							values.push(b[k]);
							if (isNaN(b[k])) {
								allNumbers = false;
							}
						}
					}
					if (values.length == 0) {
						return null;
					}
					if (allNumbers) {
						var result:Number = 0;
						for each (var v:Object in values) {
							result += Number(v);
						}
						return result;
					} else {
						return values.join("+");
					}
				}
				var o:DisplayObject = this;
				this.addEventListener("stateChanged", function() {
					o.root.doEvent(groupName + "Changed");
				});
			},
			makeStateButton: function(options:Object = null) { 
				if (this.isAlready("StateButton")) return;
				this.makeButton(options);
				options ||= {};
				var transition:Boolean = options["transition"];
				var loop:Boolean = ("loop" in options && options["loop"] || !("loop" in options));
				var o:DisplayObject = this;
				this.gotoNextState = function() {
					this.doEvent("click" + o.currentLabel.toCapitalCase());
					o.showNextDisplayState(transition, loop);
					o.doEvent("stateChanged");
					o.doEvent(o.currentLabel);
				}
				this.hit.addEventListener(MouseEvent.CLICK, function(e:MouseEvent) {
					o.gotoNextState();
				});
				this.getState = function() {
					return this.currentLabel;
				}
				this.setState = function(s:String) {
					this.doEvent("click" + this.currentLabel.toCapitalCase());
					var original:String = this.currentLabel;
					do {
						this.showNextDisplayState(transition, loop);						
					} while (this.currentLabel != s && this.currentLabel != original)
					if (this.currentLabel != s) {
						trace("cannot set state to " + s);
					} else {
						this.doEvent("stateChanged");
						this.doEvent(this.currentLabel);
					}
				}
			},
			makeTab: function(options:Object = null) {
				if (this.isAlready("Tab")) return;
				this.makeRadioButton(options);
				var o:DisplayObject = this;
				this.contentObjects ||= [];
				if ("content" in options && options.content is String) {
					for each (var s:String in options["content"].split(/\s*,\s*/)) {
						this.contentObjects.push(this.getValueForKeypath(s));
					}
				}
				this.addEventListener("selected", function() {
					for each (var tab:Object in o.radioGroup.buttons) {
						if (!tab) continue;
						if (tab != o) {
							for each (var hidden:Object in tab.contentObjects) {
								if (o.contentObjects.contains(hidden)) {
									continue;
								}
								if ("tweens" in hidden && "hide" in hidden.tweens) {
									hidden.playTween("hide");
								} else {
									hidden.hide();
								}
							}
						}
					}
					for each (var visible:Object in o.contentObjects) {
						if ("tweens" in visible && "show" in visible.tweens) {
							visible.playTween("show");
						} else {
							visible.show();
						}
					}
				});
			},
			makeRadioButton: function(options:Object = null) {
				if (this.isAlready("RadioButton")) return;
				this.makeButton(options);
				options ||= {};
				var groupName:String = options["groupName"] || "defaultRadioGroup";
				var transition:Boolean = options["transition"];
				Global.specialKeys["radioGroups"] ||= {};
				this.root["radioGroups"] ||= Global.specialKeys["radioGroups"];
				this.root["radioGroups"][groupName] ||= {};
				this.root["radioGroups"][groupName].name = groupName;
				this.root["radioGroups"][groupName].isAlready("RadioGroup");
				this.root["radioGroups"][groupName].selected ||= null;
				this.root["radioGroups"][groupName].buttons ||= [];
				this.root["radioGroups"][groupName].buttons.push(this);
				this.radioGroup = this.root["radioGroups"][groupName];
				this.root["radioGroups"][groupName].getValueForMissingKey = function(k:String) {
					if (this.selected) {
						return this.selected[k];
					} else {
						return null;
					}
				}
				var o:DisplayObject = this;
				this.select = function() {
					for (var i:int=0; i < this.root["radioGroups"][groupName].buttons.length; i++) {
						var button:DisplayObject = this.root["radioGroups"][groupName].buttons[i];
						if (button == o) {
							button.showChild("selected");
							if (button is MovieClip && "showDisplayState" in button && this.hasDisplayState("selected")) {
								button.showDisplayState("selected", transition);
							}
							if (this.root["radioGroups"][groupName].selected != button) {
								this.root["radioGroups"][groupName].selected = o;
								button.doEvent("selected");
								this.root.doEvent(groupName + "Changed");
							}
						} else {
							button.hideChild("selected");
							if (button is MovieClip && "showDisplayState" in button && this.hasDisplayState("normal")) {
								button.showDisplayState("normal", transition);
							}
							if (this.root["radioGroups"][groupName].selected == button) {
								button.doEvent("deselected");
							}
						}
					}
					this.root["radioGroups"][groupName].selected = o;
				}
				this.deselect = function() {
					if (this.root["radioGroups"][groupName].selected == this) {
						this.hideChild("selected");
						if (this is MovieClip && "showDisplayState" in this && this.hasDisplayState("normal")) {
							this.showDisplayState("normal", transition);
						}
						this.doEvent("deselected");
						this.root["radioGroups"][groupName].selected = null;
					}
				}
				this.isSelected = function() {
					return this.root["radioGroups"][groupName].selected == this;
				}
				this.parent.getSelection ||= function(groupName:String="defaultRadioGroup") {
					return this.root["radioGroups"][groupName].selected;
				}				
				this.hit.addEventListener(MouseEvent.CLICK, function(e:MouseEvent) {
					o.select();
				});
				if (options["selected"]) {
					o.addEventListener("init", function() {
						o.removeEventListener("init", arguments.callee);
						o.select();	
					});
				}
			},
			makeLink: function(options:Object = null) {
				this.makeButton(options);
			},
			makeButton: function(options:Object = null) {
				if (this.isAlready("Button")) return;
				options ||= {};
				this.labelText = this.findChild("labelText", options);
				if (this.labelText) {
					this.getLabel = function() {
						return this.labelText.text;
					};
					this.setLabel = function(label:String) {
						this.labelText.text = label;
						if (this.focusIndicator && this.focusIndicator.labelText) {
							this.focusIndicator.labelText.text = label;
						}
					};
				}
				if ("label" in options) {	
					if (this.labelText) {
						this.setLabel(options["label"]);
					} else {
						// TODO: trace no label text error
					}
				}
				this.hit = this.findChild("hit", options) || this;
				var hit:DisplayObject = this.hit;
				hit.buttonMode = true;
				hit.useHandCursor = true;
				var o:DisplayObject = this;
				hit.addEventListener(MouseEvent.CLICK, function(e:MouseEvent) {
					o.doEvent("click", {dontDispatch: true});
					if (!o.handlingAccessibilityClick) {
						// flash has set the focus to the item clicked on
						// which can be confusing for mouse only users so
						// we null it out assuming most users are either
						// mouse users or keyboard users but not both
						// ideally we could set focus back to what it was
						// previously but this does not appear to be 
						// possible
						o.stage.focus = null;
					}
				});
				this.tabEnabled = true;
				if (options && options.focusIndicatorName is String) {
					this.displayIndicatorWhenFocused(options.focusIndicatorName);
				} else {
					this.displayIndicatorWhenFocused();
				}
				this.clickOnEnter();
				this.disabledIndicator = this.findChild("disabledIndicator", options);
				this.enable = function() {
					if (o.disabledIndicator) {
						o.disabledIndicator.visible = false;
					}
					o.enabled = true;
					hit.buttonMode = true;
					hit.useHandCursor = true;
					hit.tabEnabled = true;
					hit.mouseEnabled = true;
					hit.mouseChildren = true;					
					o.doEvent("enabled");
				};
				this.disable = function() {
					if (o.disabledIndicator) {
						o.disabledIndicator.visible = true;
					}
					o.enabled = false;
					hit.buttonMode = false;
					hit.useHandCursor = false;
					hit.tabEnabled = false;
					hit.mouseEnabled = false;
					hit.mouseChildren = false;
					o.doEvent("disabled");
				};
				this.setEnabled = function(v:Object) {
					if (v) {
						o.enable();
					} else {
						o.disable();
					}
				};
				if ("enabled" in options) {
					this.setEnabled(options.enabled);
				} else {
					this.setEnabled(true);
				}
			}
		};
	}
}